package com.xinlianshiye.im.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xinlianshiye.im.ContentBody;
import com.xinlianshiye.im.RequestBody;
import com.xinlianshiye.im.ResponseBody;
import com.xinlianshiye.im.bean.App;
import com.xinlianshiye.im.bean.User;
import com.xinlianshiye.im.service.AppService;
import com.xinlianshiye.im.service.RecordService;
import com.xinlianshiye.im.service.UserService;
import com.xinlianshiye.im.utils.AuthUtil;
import com.xinlianshiye.im.utils.RedisReceiver;
import com.xinlianshiye.im.Result;
import com.xinlianshiye.im.utils.SessionManager;
import com.xinlianshiye.im.websocket.DispatcherWebSocket;
import com.xinlianshiye.im.websocket.WebSocketHandlerMethod;
import com.xinlianshiye.clouds.common.exception.WebMessageException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;

@EnableScheduling
@ServerEndpoint(value = "/websocket")
@Component
@Slf4j
public class WebSocketServer { // 每个人会分配一个独立的 实例

    static ObjectMapper objectMapper;

    @Autowired
    void setObjectMapper(ObjectMapper objectMapper) {
        WebSocketServer.objectMapper = objectMapper;
    }

    static SessionManager sessionManager;

    @Autowired
    void setSessionManager(SessionManager sessionManager) {
        WebSocketServer.sessionManager = sessionManager;
    }

    static RedisReceiver redisReceiver;

    @Autowired
    void setRedisReceiver(RedisReceiver redisReceiver) {
        WebSocketServer.redisReceiver = redisReceiver;
    }

    private static AuthUtil authUtil;

    @Autowired
    void setAuthUtil(AuthUtil authUtil) {
        WebSocketServer.authUtil = authUtil;
    }

    private static RecordService recordService;

    @Autowired
    void setRecordService(RecordService recordService) {
        WebSocketServer.recordService = recordService;
    }

    private static DispatcherWebSocket dispatcher;

    @Autowired
    void setDispatcher(DispatcherWebSocket dispatcher) {
        WebSocketServer.dispatcher = dispatcher;
    }

    private static AppService appService;

    @Autowired
    void setAppService(AppService appService) {
        WebSocketServer.appService = appService;
    }

    private static UserService userService;

    @Autowired
    void setUserService(UserService userService) {
        WebSocketServer.userService = userService;
    }

    public static final String ACCESS_TOKEN = "access_token";
    public static final String CLIENT = "client";

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) throws Exception {
        try {
            log.info("IM:>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>建立成功");
            // 解析请求中的用户信息
            Map<String, List<String>> map =session.getRequestParameterMap();
            App app = appService.get(map.get(CLIENT).get(0));
            User user = userService.verifyAccessToken(map.get(ACCESS_TOKEN).get(0), app);
            log.info("解析到用户 [appId:{}, id:{}, username:{}]", app.getAppId(), user.getId(), user.getUsername());
            userService.setUserId(session, user.getId());
            appService.setAppId(session, app.getAppId());

            sessionManager.onOpen(session);
            // 记载用户初始化信息 连接后台后 发送给前端
            userService.sendInit(user.getId());
        } catch (Exception e) {
            log.error("错误拉", e);
            session.close(new CloseReason(CloseReason.CloseCodes.NOT_CONSISTENT, "自己检查"));
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) throws IOException {
        sessionManager.onClose(session);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) throws Exception {
        RequestBody requestBody = objectMapper.readValue(message, RequestBody.class);

        Integer userId = userService.getUserId(session);
        log.info("收到请求: 用户[{}]: cmd={}, url={}, {}", userId, requestBody.getCmd(), requestBody.getUrl(), message);
        switch (requestBody.getMsgtype()) {
            case request:
                switch (requestBody.getCmd()) {
                    case chat: // 聊天记录
                        recordService.send(userId, requestBody.getReceiver(), objectMapper.readValue(requestBody.getContent(), ContentBody.class));
                        break;
                    case httprequest: //http请求
                        RequestBody httpRequestBody = objectMapper.readValue(message, RequestBody.class);
                        httpRequestBody.setReceiver(userId);
                        WebSocketHandlerMethod handlerMethod = dispatcher.findHandlerMethod(httpRequestBody.getUrl());
                        if (handlerMethod != null) {
                            Object object = handlerMethod.invoke(session, message);
                            if (object != null) {
                                if (object instanceof Result) {
                                    redisReceiver.send(session, (Result) object, httpRequestBody.getUrl());
                                } else {
                                    redisReceiver.send(session, Result.of(1, "success", object), httpRequestBody.getUrl());
                                }
                            }
                        }
                        break;
                    case ping:// 心跳机制
                        redisReceiver.send(session, ResponseBody.initPing(userService.getUserId(session)));
                        break;
                    case offline:// 关闭连接
                        this.onClose(session);
                }
                break;
        }
    }

    @OnError
    public void onError(Session session, Throwable error) throws Exception {
        if (error instanceof InvocationTargetException && ((InvocationTargetException) error).getTargetException() instanceof WebMessageException) {
            WebMessageException webMessageException = (WebMessageException) ((InvocationTargetException) error).getTargetException();
            log.warn(webMessageException.getMessage());
            redisReceiver.sendError(session, webMessageException);
        } else if (error instanceof WebMessageException) {
            WebMessageException webMessageException = (WebMessageException) error;
            log.warn(webMessageException.getMessage());
            redisReceiver.sendError(session, webMessageException);
        } else {
            log.error("捕获到一个没有处理过的异常， 请仔细检查", error);
            session.close();
        }
    }
}
